#!/usr/bin/ksh
#
# topsyscall - display top syscalls by syscall name.
#              Written using DTrace (Solaris 10 3/05).
#
# This program continually prints a report of the top system calls,
# and refreshes the display every 1 second or as specified at the
# command line.
#
# $Id: topsyscall 3 2007-08-01 10:50:08Z brendan $
#
# USAGE:        topsyscall [-Cs] [interval [count]]
#
#		    -C		# don't clear the screen
#		    -s		# print per second values
#
# FIELDS:
#		load avg	load averages, see uptime(1)
#		syscalls	total syscalls in this interval
#		syscalls/s	syscalls per second
#		SYSCALL		system call name
#		COUNT		total syscalls in this interval
#		COUNT/s		syscalls per second
#
# INSPIRATION:  top(1) by William LeFebvre
#
# COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg.
#
# CDDL HEADER START
#
#  The contents of this file are subject to the terms of the
#  Common Development and Distribution License, Version 1.0 only
#  (the "License").  You may not use this file except in compliance
#  with the License.
#
#  You can obtain a copy of the license at Docs/cddl1.txt
#  or http://www.opensolaris.org/os/licensing.
#  See the License for the specific language governing permissions
#  and limitations under the License.
#
# CDDL HEADER END
#
# 13-Jun-2005	Brendan Gregg	Created this.
# 20-Apr-2006	   "      "	Last update.
#

##############################
# --- Process Arguments ---
#

### Default variables
count=-1; interval=1; opt_persec=0; opt_clear=1

### Process options
while getopts Chs name
do
        case $name in
        C)      opt_clear=0 ;;
        s)      opt_persec=1 ;;
        h|?)    cat <<-END >&2
		USAGE: topsyscall [-s] [interval [count]]
		           -C          # don't clear the screen
		           -s          # print per second values
		   eg,
		       topsyscall      # default, 1 second updates
		       topsyscall 5    # 5 second updates
		END
		exit 1
        esac
done
shift $(( $OPTIND - 1 ))

### option logic
if [[ "$1" > 0 ]]; then
        interval=$1; shift
fi
if [[ "$1" > 0 ]]; then
        count=$1; shift
fi
if (( opt_clear )); then
        clearstr=`clear`
else
        clearstr=.
fi



#################################
# --- Main Program, DTrace ---
#
/usr/sbin/dtrace -n '
 #pragma D option quiet
 #pragma D option destructive

 /* constants */
 inline int OPT_clear  = '$opt_clear';
 inline int OPT_persec = '$opt_persec';
 inline int INTERVAL   = '$interval';
 inline int COUNTER    = '$count';
 inline int SCREEN     = 20;
 inline string CLEAR   = "'$clearstr'";

 /* variables */
 dtrace:::BEGIN
 {
	secs = INTERVAL;
	counts = COUNTER;
	printf("Tracing... Please wait.\n");
 }

 /* record syscall event */
 syscall:::entry
 {
	@Name[probefunc] = count();
	@Total = count();
 }

 /* timer */
 profile:::tick-1sec
 {
        secs--;
 }

 /* update screen */
 profile:::tick-1sec
 /secs == 0/
 {
        /* fetch load averages */
        this->load1a  = `hp_avenrun[0] / 65536;
        this->load5a  = `hp_avenrun[1] / 65536;
        this->load15a = `hp_avenrun[2] / 65536;
        this->load1b  = ((`hp_avenrun[0] % 65536) * 100) / 65536;
        this->load5b  = ((`hp_avenrun[1] % 65536) * 100) / 65536;
        this->load15b = ((`hp_avenrun[2] % 65536) * 100) / 65536;

	/* clear screen */
	OPT_clear ? printf("%s", CLEAR) : 1;

        /* print load average */
        printf("%Y, load average: %d.%02d, %d.%02d, %d.%02d",
            walltimestamp, this->load1a, this->load1b, this->load5a,
            this->load5b, this->load15a, this->load15b);

	/* calculate per second values if needed */
	OPT_persec ? normalize(@Total, INTERVAL) : 1;
	OPT_persec ? normalize(@Name, INTERVAL) : 1;

	/* print syscall count */
	printf("   %s: ", OPT_persec ? "syscalls/s" : "syscalls");
	printa("%@d\n",@Total);

	/* print report */
	trunc(@Name, SCREEN);
	printf("\n   %-25s %12s\n", "SYSCALL", 
	    OPT_persec ? "COUNT/s" : "COUNT");
	printa("   %-25s %@12d\n", @Name);
	printf("\n");

	/* reset variables */
	trunc(@Name);
	clear(@Total);
	secs = INTERVAL;
	counts--;
 }

 /*
  * End of program
  */
 profile:::tick-1sec
 /counts == 0/
 {
	exit(0);
 }

 /*
  * Cleanup for Ctrl-C
  */
 dtrace:::END
 {
	trunc(@Name);
	trunc(@Total);
 }
'

